package com.sec.etech.common;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import groovy.lang.Tuple2;
import org.assertj.core.util.Sets;

import java.math.BigDecimal;
import java.util.*;

public class JsonUtil {

    public static  <T>  T toJavaObject(JSONObject obj, Class<T> clazz){
        return JSONObject.toJavaObject(obj, clazz);
    }

    public static JSONObject fromJavaObject(Object obj){
        return  new JSONObject(BeanUtil.beanToMap(obj));
    }

    public static synchronized Object put(JSONObject json, String keyChain, Object value){
        if (!keyChain.contains(".")){
            return json.put(keyChain, value);
        }else{
            List<String> keys = Arrays.asList(keyChain.split("\\."));
            JSONObject newJson = runToTarget(json, keys);
            return newJson.put(keys.get(keys.size() - 1 ), value);
        }
    }

    private static JSONObject runToTarget(JSONObject json,List<String> keys){
        JSONObject newJson = json;
        for (String k : keys.subList(0, keys.size()-1)){
            if (k.equals("$1")){
                Set<String> subkeys = newJson.keySet();
                if (!newJson.isEmpty()){
                    newJson = newJson.getJSONObject(subkeys.stream().findFirst().get());
                }else{
                    return null;
                }
            }else if (k.equals("$2")){
                Set<String> subkeys = newJson.keySet();
                if (!newJson.isEmpty()){
                    newJson = newJson.getJSONObject(subkeys.stream().skip(1).findFirst().get());
                }else{
                    return null;
                }
            } else{
                newJson = newJson.getJSONObject(k);
            }

        }
        return newJson;
    }

    public static String getString(JSONObject json, String keyChain, String defaultVal){
        String res = null;
        if (!keyChain.contains(".")){
            if (keyChain.contains("$1")){
                Set<String> subkeys = json.keySet();
                if (!json.isEmpty()){
                    return json.getString(subkeys.stream().findFirst().get());
                }
            }
            res = json.getString(keyChain);
        }else{
            List<String> keys = Arrays.asList(keyChain.split("\\."));
            JSONObject newJson = runToTarget(json, keys);
            res = newJson.getString(keys.get(keys.size() - 1 ));
        }
        if (res == null){
            return defaultVal;
        }
        return res;
    }
    public static String getString(JSONObject json, String keyChain){
        if (!keyChain.contains(".")){
            if (keyChain.contains("$1")){
                Set<String> subkeys = json.keySet();
                if (!json.isEmpty()){
                    return json.getString(subkeys.stream().findFirst().get());
                }
            }
            return json.getString(keyChain);
        }else{
            List<String> keys = Arrays.asList(keyChain.split("\\."));
            JSONObject newJson = runToTarget(json, keys);
            return newJson.getString(keys.get(keys.size() - 1 ));
        }
    }

    public static Long getLong(JSONObject json, String keyChain){
        if (!keyChain.contains(".")){
            if (keyChain.contains("$1")){
                Set<String> subkeys = json.keySet();
                if (!json.isEmpty()){
                    return json.getLong(subkeys.stream().findFirst().get());
                }
            }
            return json.getLong(keyChain);
        }else{
            List<String> keys = Arrays.asList(keyChain.split("\\."));
            JSONObject newJson = runToTarget(json, keys);
            return newJson.getLong(keys.get(keys.size() - 1 ));
        }
    }

    public static JSONObject getJson(JSONObject json, String keyChain){
        if (!keyChain.contains(".")){
            String key = keyChain;
            if (keyChain.contains("$1")){
                Set<String> subkeys = json.keySet();
                if (!json.isEmpty()){
                    return json.getJSONObject(subkeys.stream().findFirst().get());
                }
            }
            return json.getJSONObject(keyChain);
        }else{
            List<String> keys = Arrays.asList(keyChain.split("\\."));
            JSONObject newJson = runToTarget(json, keys);
            return newJson.getJSONObject(keys.get(keys.size() - 1 ));
        }
    }

    public static Object getObject(JSONObject json, String keyChain){
        if (!keyChain.contains(".")){
            if (keyChain.contains("$1")){
                Set<String> subkeys = json.keySet();
                if (!json.isEmpty()){
                    return json.get(subkeys.stream().findFirst().get());
                }
            }
            return json.get(keyChain);
        }else{
            List<String> keys = Arrays.asList(keyChain.split("\\."));
            JSONObject newJson = runToTarget(json, keys);
            return newJson.get(keys.get(keys.size() - 1 ));
        }
    }

    public static class JsonDiff{
        public List<String> fieldArr = new ArrayList<>();
        // 异常字段集
        public Map<String, Object> bMinusAndNull = new HashMap<>();
        // 正常字段集
        public Map<String, Object> cMinusAndNull = new HashMap<>();
        // 不同的字段集
        public Map<String, Tuple2> dataDiff = new HashMap<>();
        public Map<String, Object> dataDiffDesc = new HashMap<>();

        public boolean needError = false;

        public List<JsonDiff> subDiff = new ArrayList<>();

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("JsonDiff: " + fieldArr + ",");
            if (!bMinusAndNull.isEmpty()){
                sb.append("  bMinusAndNull: " + bMinusAndNull + ",");
            }
            if (!cMinusAndNull.isEmpty()){
                sb.append("  cMinusAndNull: " + cMinusAndNull + ",");
            }
            if (!dataDiff.isEmpty()){
                sb.append("  dataDiff: " + dataDiff + ",");
            }
            sb.append("  subDiff: " + subDiff + ",");
            sb.append("  needError: " + needError + ".");
            return sb.toString();
        }

        public boolean isEmpty(){
            return fieldArr.isEmpty() && bMinusAndNull.isEmpty() && cMinusAndNull.isEmpty() && dataDiff.isEmpty() && subDiff.isEmpty();
        }
    }


    public static JsonDiff compareJson(JSONObject origin, JSONObject target, Set<String> ignoreField){

        return compareObject(origin, target, new JsonDiff(), ignoreField);
    }

    public static JsonDiff compareJson(JSONObject origin, JSONObject target){

        return compareObject(origin, target, new JsonDiff(), Sets.set("lastuid", "lastdate"));
    }

    public static void main(String[] args) {

    }

    private static boolean equals(Object obj1, Object obj2){

        if ((obj1 instanceof Double || obj2 instanceof Double) && obj1 != null && obj2 != null){
            if ( Math.abs(new BigDecimal(obj1.toString()).doubleValue() - new BigDecimal(obj2.toString()).doubleValue() ) < 0.0001){
                return true;
            }
        }

        boolean b = Objects.equals(obj1, obj2);
        if (b){
            return true;
        }else {
            if (obj1 != null && obj2 != null){
                return obj1.toString().equals(obj2.toString());
            }else{
                return false;
            }
        }
    }


    private static JsonDiff compareObject(JSONObject origin, JSONObject target, JsonDiff diff, Set<String> ignoreField){

        Set<String> originKeys = origin.keySet();
        Set<String> targetKeys = target.keySet();

        Set<String> originNeedRemove = new HashSet<>();

        for (String ss : originKeys){
            if (origin.get(ss) == null){
                originNeedRemove.add(ss);
            }
        }



        //B-C = B-A => 异常情况，很少出现后者把前者干掉
        Set<String> subSet1 = new HashSet<>(originKeys);
        subSet1.removeAll(originNeedRemove);
        subSet1.removeAll(targetKeys);
        if (!subSet1.isEmpty()) {
            for (String s : subSet1){
                if (ignoreField.contains(s)){
                    continue;
                }
                diff.bMinusAndNull.put(s, origin.get(s));
            }
//            System.out.println("异常情况A >>: " + subSet1);
        }

        // C-B = C-A => 正常情况
        Set<String> subSet2 = new HashSet<>(targetKeys);
        subSet2.removeAll(originKeys);
        if (!subSet2.isEmpty()){
            for (String s : subSet2){
                if (ignoreField.contains(s)){
                    continue;
                }
                //if (target.get(s) != null){
                    diff.cMinusAndNull.put(s, target.get(s));
                //}

            }
            System.out.println("正常情况B >>: " + subSet2);
        }


        // 并集
        Set<String> subSet3 = new HashSet<>(targetKeys);
        subSet3.retainAll(originKeys);


        for (String key: subSet3){
            if (ignoreField.contains(key)){
                continue;
            }
            Object originVal = origin.get(key);
            Object targetVal = target.get(key);

            if (targetVal != null && originVal == null){
                diff.cMinusAndNull.put(key, targetVal);
            }
            if (targetVal == null && originVal != null){
                diff.bMinusAndNull.put(key, originVal);
            }
            if (targetVal != null && originVal != null){
                if (targetVal instanceof JSONObject && targetVal instanceof JSONObject){
                    JsonDiff diff2 = new JsonDiff();
                    diff2.fieldArr.addAll(diff.fieldArr);
                    diff2.fieldArr.add(key);
                    diff.subDiff.add(diff2);
                    diff.needError = diff.needError || diff2.needError;
                    compareObject((JSONObject) originVal,(JSONObject)  targetVal, diff2, ignoreField);
                }
                else if (targetVal instanceof JSONArray){
                    JSONArray t1 = (JSONArray) targetVal;
                    JSONArray o1 = (JSONArray) originVal;

                    System.out.println(">> V1 " + t1);
                    System.out.println(">> V2 " + o1);

                    if (t1.size() == o1.size()){
                        // 假设2个长度一样
                        int length = t1.size();
                        for (int i = 0; i < length; i++){
                            JSONObject t1v = t1.getJSONObject(i);

                            for (int j = 0; j < length; j++){
                                JSONObject o1v = o1.getJSONObject(j);
                                if (t1v.containsKey("id") && Objects.equals(t1v.getString("id"), o1v.getString("id"))){
                                    // 可以比较
                                    JsonDiff diff3 = compareObject(o1v, t1v, new JsonDiff(), Sets.set("lastuid", "lastdate"));

                                    if (!diff3.bMinusAndNull.isEmpty()){
                                        diff.needError = true;
                                    }


                                    System.out.println("<>>> " + diff3);
                                    System.out.println("<>>> " + diff);


                                }


                            }

                        }
                    }

                    if (o1.size() > t1.size()){
                        diff.needError = true;
                    }



                    // TODO JSONaArray 先不管
                }else{
                    boolean isEqual = equals(targetVal, originVal);
                    if (!isEqual){
                        diff.dataDiff.put(key, Tuple2.tuple(originVal, targetVal));
                        diff.dataDiffDesc.put(key, originVal + " => " + targetVal);
                    }
                }

            }

        }


        return diff;
    }


}
